//
// Created by shirley on 2024/12/23.
//

#include "DenseMatrix.h"

DenseMatrix::DenseMatrix()
        : rows(0),
          cols(0),
          data(0, vector<double>(0, 0))
{
}

DenseMatrix::DenseMatrix(size_t rows, size_t cols, double initValue)
        : rows(rows),
          cols(cols),
          data(rows, vector<double>(cols, initValue))
{
}


DenseMatrix::DenseMatrix(const DenseMatrix& matrix)
{
    this->rows = matrix.rows;
    this->cols = matrix.cols;
    this->data = matrix.data;
}

DenseMatrix::DenseMatrix(size_t rows, size_t cols, double* data)
{
    this->rows = rows;
    this->cols = cols;
    this->data = vector<vector<double>>(rows, vector<double>(cols));
    for (size_t i = 0; i < rows; ++i)
    {
        for (size_t j = 0; j < cols; ++j)
            this->data[i][j] = data[i * cols + j];
    }
}

DenseMatrix::DenseMatrix(const DenseMatrix& matrix, size_t startRow, size_t startCol, size_t numRows, size_t numCols)
    : rows(numRows), cols(numCols)
{
    if (startRow + numRows > matrix.getRows() || startCol + numCols > matrix.getCols())
    {
        throw std::invalid_argument("Invalid block dimensions");
    }

    data.resize(rows);
    for (size_t i = 0; i < rows; ++i)
    {
        data[i].resize(cols);
        for (size_t j = 0; j < cols; ++j)
        {
            data[i][j] = matrix.GetData()[startRow + i][startCol + j];
        }
    }
}

void DenseMatrix::setValue(size_t row, size_t col, double value)
{
    if (row < rows && col < cols)
    {
        data[row][col] = value;
    }
    else
    {
        cerr << "Index out of bounds." << endl;
    }
}

double DenseMatrix::getValue(size_t row, size_t col) const
{
    if (row < rows && col < cols)
    {
        return data[row][col];
    }
    else
    {
        cerr << "Index out of bounds." << endl;
        return 0.0;
    }
}

vector<DenseMatrix> DenseMatrix::GetBlocks() const
{
    return blocks;
}

void DenseMatrix::swap_(size_t row1, size_t col1, size_t row2, size_t col2)
{
    swap(data[row1][col1], data[row2][col2]);
}

DenseMatrix DenseMatrix::operator+(const DenseMatrix& other) const
{
    if (rows != other.rows || cols != other.cols)
    {
        cerr << "Matrix dimensions do not match for addition." << endl;
        return DenseMatrix(0, 0);
    }

    DenseMatrix result(rows, cols);
    for (size_t i = 0; i < rows; ++i)
    {
        for (size_t j = 0; j < cols; ++j)
        {
            result.data[i][j] = data[i][j] + other.data[i][j];
        }
    }
    return result;
}

DenseMatrix DenseMatrix::operator*(const DenseMatrix& other) const
{
    if (cols != other.rows)
    {
        cerr << "Matrix dimensions do not match for multiplication." << endl;
        return DenseMatrix(0, 0);
    }

    DenseMatrix result(rows, other.cols);
    for (size_t i = 0; i < rows; ++i)
    {
        for (size_t j = 0; j < other.cols; ++j)
        {
            for (size_t k = 0; k < cols; ++k)
            {
                result.data[i][j] += data[i][k] * other.data[k][j];
            }
        }
    }
    return result;
}

void DenseMatrix::block_(size_t size)
{
    if (size == 0) {
        throw invalid_argument("Block size cannot be zero.");
    }
    // 计算每个子矩阵的行数和列数
    const auto blocks_rows = (rows + size - 1) / size;
    const auto blocks_cols = (cols + size - 1) / size;

    // 初始化分块矩阵
    blocks.resize(blocks_rows * blocks_cols);
    //生成子矩阵并存储到blocks中
    for (size_t i = 0; i < blocks_rows; ++i)
    {
        for (size_t j = 0; j < blocks_cols; ++j)
        {
            // 计算当前子块的行数和列数
            size_t blockRows = std::min(size, rows - i * size);
            size_t blockCols = std::min(size, cols - j * size);
            DenseMatrix block(blocks_rows, blocks_cols);

            for (size_t row = 0; row < blockRows; ++row)
            {
                for (size_t col = 0; col < blockCols; ++col)
                {
                    block.setValue(row, col, getValue(i * size + row, j * size + col));
                }
            }
            blocks[i] = block;
        }
    }
}

void DenseMatrix::print() const
{
    for (size_t i = 0; i < rows; ++i)
    {
        for (size_t j = 0; j < cols; ++j)
        {
            cout << data[i][j] << " ";
        }
        cout << endl;
    }
}
